<?php

namespace lianlianwechat\lib;


use think\facade\Config;
use think\facade\Log;

class ToolServer
{
    private static $instance;

    private function __construct()
    {

    }

    public static function getInstance($param = [], $type='app')
    {
        if (!(self::$instance instanceof self)) {
            self::$instance = new self();
            self::$instance->type = $type;
            self::$instance->config = $param;
        }
        return self::$instance;
    }




    /**
     * 生成要请求给连连支付的参数数组
     *
     * @param $para_temp 请求前的参数数组
     *
     * @return 要请求的参数数组
     */
    public static function buildRequestPara($para_temp)
    {
        $para_temp = array_merge(self::$instance->config[self::$instance->type], $para_temp);
        //除去待签名参数数组中的空值和签名参数
        $para_filter = self::paraFilter($para_temp);
        //对待签名参数数组排序
        $para_sort = self::argSort($para_filter);
        //生成签名结果
        $mysign = self::buildRequestMysign($para_sort);
        //签名结果与签名方式加入请求提交参数组中
        $para_sort['sign']      = $mysign;
        $para_sort['sign_type'] = self::$instance->config[self::$instance->type]['sign_type'];

        Log::record('============连连支付请求参数===============');
        Log::record($para_sort);

        foreach ($para_sort as $key => $value) {
            $para_sort[$key] = $value;
        }
        return $para_sort;
    }

    /**
     * 验证签名
     */
    public static function checkSgin($para_temp)
    {
        //除去待签名参数数组中的空值和签名参数
        $para_filter = self::paraFilter($para_temp);
        //对待签名参数数组排序
        $para_sort = self::argSort($para_filter);

        $prestr = self::createLinkstring($para_sort);
        //生成签名结果
        return self::Rsaverify($prestr, $para_temp['sign']);
    }

    /**
     * 对数组排序
     *
     * @param $para 排序前的数组
     *              return 排序后的数组
     */
    public static function argSort($para)
    {
        ksort($para);
        reset($para);

        return $para;
    }

    public static function createLinkstring($para)
    {
        $arg = "";
        foreach($para as $key=>$val) {
            $arg .= $key."=".$val."&";
        }
        $arg = substr($arg, 0, strlen($arg) - 2);
        //如果存在转义字符，那么去掉转义
        if (get_magic_quotes_gpc()) {
            $arg = stripslashes($arg);
        }

        return $arg;
    }

    public static function paraFilter($para)
    {
        $para_filter = [];
        foreach($para as $key=>$val) {
            if ($key == "sign" || $val === "") {
                continue;
            }else {
                $para_filter[$key] = $para[$key];
            }
        }
        return $para_filter;
    }

    /**
     * 生成签名结果
     *
     * @param $para_sort 已排序要签名的数组
     *                   return 签名结果字符串
     */
    public static function buildRequestMysign($para_sort)
    {
        //把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
        $prestr = self::createLinkstring($para_sort);
        $mysign = "";
        switch (self::$instance->config[self::$instance->type]['sign_type']) {
            case "RSA" :
                $mysign = self::RsaSign($prestr);
                break;
            default :
                $mysign = "";
                break;
        }

        return $mysign;
    }


    /**
     * 远程获取数据，POST模式
     * 注意：
     *
     * @param $url  指定URL完整路径地址
     * @param $para 请求的数据
     *              return 远程输出的数据
     */
    public static function getHttpResponseJSON($para)
    {
        $json = json_encode($para);
        $curl = curl_init(self::$instance->config['pay_url']);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); //信任任何证书
        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "POST");
        curl_setopt($curl, CURLOPT_POSTFIELDS, $json);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_HTTPHEADER, [
                'Content-Type: application/json',
                'Content-Length: '.strlen($json),
            ]);
        $responseText = curl_exec($curl);
        curl_close($curl);
        return json_decode($responseText, true);
    }

    /**
     * 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串，并对字符串做urlencode编码
     *
     * @param $para 需要拼接的数组
     *              return 拼接完成以后的字符串
     */
    public static function createLinkstringUrlencode($para)
    {
        $arg = "";
        foreach ($para as $key=>$val) {
            $arg .= $key."=".urlencode($val)."&";
        }
        //去掉最后一个&字符
        $arg = substr($arg, 0, strlen($arg) - 2);
        //如果存在转义字符，那么去掉转义
        if (get_magic_quotes_gpc()) {
            $arg = stripslashes($arg);
        }
        return $arg;
    }
    /********************************************************************************/

    /**RSA签名
     * $data签名数据(需要先排序，然后拼接)
     * 签名用商户私钥，必须是没有经过pkcs8转换的私钥
     * 最后的签名，需要用base64编码
     * return Sign签名
     */

    public static function Rsasign($data)
    {
        //转换为openssl密钥，必须是没有经过pkcs8转换的私钥
        $res = openssl_get_privatekey(file_get_contents(self::$instance->config['RSA_PRIVATE_KEY']));

        //调用openssl内置签名方法，生成签名$sign
        openssl_sign($data, $sign, $res, OPENSSL_ALGO_MD5);

        //释放资源
        openssl_free_key($res);

        //base64编码
        $sign = base64_encode($sign);

        //file_put_contents("log.txt","签名原串:".$data."\n", FILE_APPEND);
        return $sign;
    }

    /********************************************************************************/

    /**RSA验签
     * $data待签名数据(需要先排序，然后拼接)
     * $sign需要验签的签名,需要base64_decode解码
     * 验签用连连支付公钥
     * return 验签是否通过 bool值
     */
    public static function Rsaverify($data, $sign)
    {
        //读取连连支付公钥文件
        $pubKey = file_get_contents(env('extend_path').'lianlian/key/llpay_public_key.pem');

        //转换为openssl格式密钥
        $res = openssl_get_publickey($pubKey);

        //调用openssl内置方法验签，返回bool值
        $result = (bool)openssl_verify($data, base64_decode($sign), $res, OPENSSL_ALGO_MD5);

        //释放资源
        openssl_free_key($res);

        //返回资源是否成功
        return $result;
    }

    /**
     * 签名字符串
     *
     * @param $prestr 需要签名的字符串
     * @param $key    私钥
     *                return 签名结果
     */
    public static function md5Sign($prestr, $key)
    {
        $logstr = $prestr."&key=************************";
        $prestr = $prestr."&key=".$key;
        return md5($prestr);
    }


    private function __clone()
    {

    }

}